package com.flying.invest.strategy.service;

import java.text.DecimalFormat;
import java.util.Date;

import com.flying.common.annotation.DaoCreate;
import com.flying.common.annotation.DaoQuery;
import com.flying.common.annotation.DaoRemove;
import com.flying.common.annotation.DaoUpdate;
import com.flying.common.annotation.Transaction;
import com.flying.common.util.Codes;
import com.flying.common.util.DateUtils;
import com.flying.common.util.DateUtils.Field;
import com.flying.framework.annotation.MethodInfo;
import com.flying.framework.annotation.Param;
import com.flying.framework.annotation.Service;
import com.flying.framework.context.ServiceContext;
import com.flying.framework.model.ActionResult;
import com.flying.framework.model.ModelFactory;
import com.flying.framework.model.QueryResult;
import com.flying.framework.security.Principal;
import com.flying.framework.service.AbstractService;
import com.flying.invest.assets.RiskLevel;
import com.flying.invest.invest.TransactionType;
import com.flying.invest.invest.model.Investment;
import com.flying.invest.invest.model.InvestmentSuggestion;
import com.flying.invest.invest.service.InvestmentService;
import com.flying.invest.invest.service.InvestmentSuggestionService;
import com.flying.invest.invest.service.InvestmentTransactionService;
import com.flying.invest.strategy.model.Strategy;
import com.flying.invest.strategy.model.StrategyInvestMonitor;
import com.flying.invest.strategy.model.Strategy.Status;
import com.flying.invest.summary.service.SummaryInvestDailyService;

@Service(value="StrategyInvestMonitorService", desc="")
public class StrategyInvestMonitorService extends AbstractService implements IStrategyService, ITransactionCallbackService{
	private StrategyService strategyService;
	private InvestmentService investService;
	private InvestmentTransactionService transactionService;
	private InvestmentSuggestionService suggestionService;
	private StrategyInvestMonitorDetailService detailService;
	private StrategyInvestMonitorTransactionService monintorTransactionService;
	private SummaryInvestDailyService summaryService;
	
	public void setStrategyService(StrategyService s) {
		this.strategyService = s;
	}
	public void setInvestmentService(InvestmentService s) {
		this.investService = s;
	}
	public void setInvestmentTransactionService(InvestmentTransactionService s) {
		this.transactionService = s;
	}
	public void setInvestmentSuggestionService(InvestmentSuggestionService s) {
		this.suggestionService = s;
	}
	public void setStrategyInvestMonitorDetailService(StrategyInvestMonitorDetailService s) {
		this.detailService = s;
	}
	public void setStrategyInvestMonitorTransactionService(StrategyInvestMonitorTransactionService s) {
		this.monintorTransactionService = s;
	}
	public void setSummaryInvestDailyService(SummaryInvestDailyService s) {
		this.summaryService = s;
	}
        
	@MethodInfo("新增")
	@DaoCreate(entity=StrategyInvestMonitor.ENTITY_NAME)
	protected StrategyInvestMonitor create2(
			@Param(value=StrategyInvestMonitor.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id,
			@Param(value=StrategyInvestMonitor.INVEST_STATUS, required=false,	desc="状态", enumClass = Investment.Status.class) Integer invest_status,
			@Param(value=StrategyInvestMonitor.ASSETS_TYPE_ID, required=false,	desc="类型ID") Long assets_type_id,
			@Param(value=StrategyInvestMonitor.ASSETS_MARKET_ID, required=false,	desc="市场ID") Long assets_market_id,
			@Param(value=StrategyInvestMonitor.ASSETS_BLOCK_ID, required=false,	desc="板块ID") Long assets_block_id,
			
			@Param(value=StrategyInvestMonitor.MAX_PROFIT_RATE, required=false,	desc="最高盈利率") Double max_profit_rate,
			@Param(value=StrategyInvestMonitor.MAX_LOSS_RATE, required=false,	desc="最大回撤率") Double max_loss_rate,
			@Param(value=StrategyInvestMonitor.CHECK_LIMIT_RATE, required=false,	desc="是否检查配额") Integer check_limit_rate,
			@Param(value=StrategyInvestMonitor.COMMISSION_RATE, required=true,	desc="委托比例") Double commission_rate
		) {
		return null;
	}

	@SuppressWarnings("unchecked")
	@Override
	public StrategyInvestMonitor createSub(Long strategy_id) {
		return null;
	}
    
	@MethodInfo("新增")
	@Transaction
	public StrategyInvestMonitor create(
			@Param(value=Strategy.NAME, required=true,	desc="名称") String name,
			@Param(value=Strategy.REMARKS, required=false,	desc="说明") String remarks,
			@Param(value=Strategy.INVEST_ID, required=false,	desc="投资项目") Long invest_id,
			@Param(value=Strategy.STATUS, required=true,	desc="状态", enumClass=Status.class) Integer status,
			
			@Param(value=StrategyInvestMonitor.INVEST_STATUS, required=false,	desc="状态", enumClass = Investment.Status.class) Integer invest_status,
			@Param(value=StrategyInvestMonitor.ASSETS_TYPE_ID, required=false,	desc="类型ID") Long assets_type_id,
			@Param(value=StrategyInvestMonitor.ASSETS_MARKET_ID, required=false,	desc="市场ID") Long assets_market_id,
			@Param(value=StrategyInvestMonitor.ASSETS_BLOCK_ID, required=false,	desc="板块ID") Long assets_block_id,
			@Param(value=StrategyInvestMonitor.MAX_PROFIT_RATE, required=false,	desc="最高盈利率") Double max_profit_rate,
			@Param(value=StrategyInvestMonitor.MAX_LOSS_RATE, required=false,	desc="最大回撤率") Double max_loss_rate,
			@Param(value=StrategyInvestMonitor.CHECK_LIMIT_RATE, required=false,	desc="是否检查配额") Integer check_limit_rate,
			@Param(value=StrategyInvestMonitor.COMMISSION_RATE, required=true,	desc="委托比例") Double commission_rate
		) {
		Long assets_id = null;
		var type_id = 8L;
		if(invest_id != null) {
			assets_id = this.investService.findById(invest_id).getAssetsId();
		}
		var strategy = this.strategyService.create2(null, type_id, invest_id, assets_id, name, remarks, 
				null, status, null, null, null, null);
		return this.create2(strategy.getStrategyId(), invest_status, assets_type_id, assets_market_id, assets_block_id, 
				max_profit_rate, max_loss_rate, check_limit_rate, commission_rate);
	}

	@MethodInfo("修改")
	@DaoUpdate(entity=StrategyInvestMonitor.ENTITY_NAME)
	public ActionResult update2 (
			@Param(value=StrategyInvestMonitor.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id,
			
			@Param(value=StrategyInvestMonitor.INVEST_STATUS, required=false,	desc="状态", enumClass = Investment.Status.class) Integer invest_status,
			@Param(value=StrategyInvestMonitor.ASSETS_TYPE_ID, required=false,	desc="类型ID") Long assets_type_id,
			@Param(value=StrategyInvestMonitor.ASSETS_MARKET_ID, required=false,	desc="市场ID") Long assets_market_id,
			@Param(value=StrategyInvestMonitor.ASSETS_BLOCK_ID, required=false,	desc="板块ID") Long assets_block_id,
			@Param(value=StrategyInvestMonitor.MAX_PROFIT_RATE, required=false,	desc="最高盈利率") Double max_profit_rate,
			@Param(value=StrategyInvestMonitor.MAX_LOSS_RATE, required=false,	desc="最大回撤率") Double max_loss_rate,
			@Param(value=StrategyInvestMonitor.CHECK_LIMIT_RATE, required=false,	desc="是否检查配额") Integer check_limit_rate,
			@Param(value=StrategyInvestMonitor.COMMISSION_RATE, required=true,	desc="委托比例") Double commission_rate
		) {
		return null;
	}

	@MethodInfo("修改")
	@Transaction
	public ActionResult update(
			@Param(value=StrategyInvestMonitor.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id,
			@Param(value=Strategy.NAME, required=true,	desc="名称") String name,
			@Param(value=Strategy.REMARKS, required=false,	desc="说明") String remarks,
			@Param(value=Strategy.INVEST_ID, required=false,	desc="投资项目") Long invest_id,
			@Param(value=Strategy.STATUS, required=true,	desc="状态", enumClass=Status.class) Integer status,
			
			@Param(value=StrategyInvestMonitor.INVEST_STATUS, required=false,	desc="状态", enumClass = Investment.Status.class) Integer invest_status,
			@Param(value=StrategyInvestMonitor.ASSETS_TYPE_ID, required=false,	desc="类型ID") Long assets_type_id,
			@Param(value=StrategyInvestMonitor.ASSETS_MARKET_ID, required=false,	desc="市场ID") Long assets_market_id,
			@Param(value=StrategyInvestMonitor.ASSETS_BLOCK_ID, required=false,	desc="板块ID") Long assets_block_id,
			@Param(value=StrategyInvestMonitor.MAX_PROFIT_RATE, required=false,	desc="最高盈利率") Double max_profit_rate,
			@Param(value=StrategyInvestMonitor.MAX_LOSS_RATE, required=false,	desc="最大回撤率") Double max_loss_rate,
			@Param(value=StrategyInvestMonitor.CHECK_LIMIT_RATE, required=false,	desc="是否检查配额") Integer check_limit_rate,
			@Param(value=StrategyInvestMonitor.COMMISSION_RATE, required=true,	desc="委托比例") Double commission_rate
		) {
		this.strategyService.update(strategy_id, name, remarks, 
				null, status, null, null, 
				null, null, null);
		if(invest_id != null) {
			var invest = this.investService.findById(invest_id);
			this.strategyService.updateInvestInfo(strategy_id, invest_id, invest.getAssetsId());
		}
		return this.update2(strategy_id, invest_status, assets_type_id, assets_market_id, assets_block_id, max_profit_rate, max_loss_rate, check_limit_rate, commission_rate);
	}

	@MethodInfo("删除")
	@DaoRemove(entity=StrategyInvestMonitor.ENTITY_NAME)
	public ActionResult remove(
			@Param(value=StrategyInvestMonitor.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id
		) {
		return null;
	}

	@SuppressWarnings("unchecked")
	@MethodInfo("搜索")
	@DaoQuery(entity=StrategyInvestMonitor.ENTITY_NAME, pageable=true, modelClass=StrategyInvestMonitor.class,
			qsql="select * from v_strategy_invest_monitor t", csql="select count(0) as c from v_strategy_invest_monitor t", osql="order by t.max_profit_rate desc")
	public QueryResult<StrategyInvestMonitor> findAll(
			@Param(value=Strategy.USER_ID, required=false,	desc="所属用户") Long user_id,
			@Param(value=Strategy.NAME, required=false,	desc="名称", tag="LIKE") String name,
			@Param(value=Strategy.INVEST_ID, required=false,	desc="投资项目") Long invest_id,
			@Param(value=Strategy.STATUS, required=false,	desc="状态", enumClass=Status.class) Integer status,
			
			@Param(value=Codes.PAGE_NUM, 	required=false, desc="分页页号，默认0") int page,
			@Param(value=Codes.PAGE_SIZE, 	required=false, desc="分页大小，默认10") int rows) {
		Principal p = ServiceContext.getContext().getPrincipal();
		if(user_id == null && p != null) user_id = Long.parseLong(p.getId());
		return ModelFactory.create(QueryResult.class, Strategy.USER_ID, user_id);
	}

	@SuppressWarnings("unchecked")
	@MethodInfo("查询")
	@DaoQuery(entity=StrategyInvestMonitor.ENTITY_NAME, single=true, throwsNotFoundException=true, 
	qsql="select * from v_strategy_invest_monitor t" )
	public StrategyInvestMonitor findById(
			@Param(value=StrategyInvestMonitor.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id
		) {
		return null;
	}
	
	//
	@Override
	public ActionResult transactionCallback(Long strategy_id, Long transaction_id, Integer transaction_status,
			Integer transaction_type, Double quantity, Double price, Double amount) {
		var mon = this.findById(strategy_id);
		var trans = this.transactionService.findById(transaction_id);
		var sugg = this.suggestionService.findById(trans.getSuggestionId());
		var detail_id = Long.parseLong(sugg.getExtra());
		var detail = this.detailService.findById(detail_id);
		this.monintorTransactionService.create(transaction_id, detail.getUserId(), mon.getTypeId(), detail.getInvestId(), strategy_id, 
				detail.getTransactionType(), amount, quantity, price, new Date());
		return this.detailService.updateByTransaction(strategy_id, amount, quantity, price, transaction_id, new Date());
	}
	
	@Override
	public ActionResult executeStrategy(Long strategy_id) {
		var mon = this.findById(strategy_id);
		var invest_id = mon.getInvestId();
		if(invest_id != null) {
			return this.executeStrategyForInvest(strategy_id, invest_id);
		} else {
			Integer[] status = mon.getInvestStatus() == null?null: new Integer[] {mon.getInvestStatus()};
			var invests = this.investService.findForMonitor(mon.getUserId(), status, mon.getAssetsId(), mon.getAssetsTypeId(), mon.getAssetsMarketId(), mon.getAssetsBlockId(), 0, Integer.MAX_VALUE).getRows();
			invests.stream().forEach(x -> {
				this.executeStrategyForInvest(strategy_id, x.getInvestId());
			});
		}
		return null;
	}
	
	public ActionResult executeStrategyForInvest(
			@Param(value=StrategyInvestMonitor.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id,
			@Param(value=Strategy.INVEST_ID, required=false,	desc="投资项目") Long invest_id) {
		var existsSuggestions = this.suggestionService.findByStrategyTypeIdAndInvestId(8L, invest_id, 
				new Integer[] {InvestmentSuggestion.Status.ORDERED.value, InvestmentSuggestion.Status.PREPARING.value}).getRows();
		if(!existsSuggestions.isEmpty()) {
			return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS, Codes.MSG, "Suggestion exists!");
		}
		var invest = this.investService.findById(invest_id);
		if(invest.getPrice() == null) {
			return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS, Codes.MSG, invest.getName() + " price is null, ignored!");
		}
		if(invest.getAssets().getAssetsType().getRiskLevel() <= RiskLevel.LOW.value()) {
			return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS, Codes.MSG, invest.getName() + " risk level is low, ignored!");
		}
		var mon = this.findById(strategy_id);
		var nf = new DecimalFormat("0.00");
		var batchNum = mon.getBatchNum() == null?0:mon.getBatchNum();
		if(mon.getMaxLossRate() == null && mon.getMaxProfitRate() != null) {	//止盈
			var actualRate = (invest.getAmount() - invest.getCost()) / invest.getCost();
			if(actualRate > mon.getMaxProfitRate()) {
				var inv = new Invest(invest.getAmount() * mon.getCommissionRate(), invest.getPrice(), 100.0);
				var detail = this.detailService.create(mon.getUserId(), mon.getTypeId(), invest_id, strategy_id, 
						TransactionType.SELL.value(), inv.getAmount(), inv.getQuantity(), invest.getPrice(), new Date());
				var remarks = "["+mon.getName()+"]" + "止盈监控，当前浮盈：["+nf.format(actualRate * 100)+"%]大于设定止盈浮盈：["+nf.format(mon.getMaxProfitRate() * 100)+"%]，建议卖出!";
				this.suggestionService.create(mon.getUserId(), invest_id, strategy_id, batchNum + 1, invest.getName(), remarks, String.valueOf(detail.getDetailId()), 
						TransactionType.SELL.value, inv.getAmount(), inv.getQuantity(), 
						new Date(), DateUtils.add(new Date(), Field.DATE, 5), null, "system", new Date());
			}
		} else if(mon.getMaxLossRate() != null && mon.getMaxProfitRate() == null) { 	//止损
			var actualRate = (invest.getCost() - invest.getAmount()) / invest.getCost();
			if(actualRate > mon.getMaxLossRate()) {
				var inv = new Invest(invest.getAmount() * mon.getCommissionRate(), invest.getPrice(), 100.0);
				var detail = this.detailService.create(mon.getUserId(), mon.getTypeId(), invest_id, strategy_id, 
						TransactionType.SELL.value(), inv.getAmount(), inv.getQuantity(), invest.getPrice(), new Date());
				var remarks = "["+mon.getName()+"]" + "止损监控，当前浮亏：[-"+nf.format(actualRate * 100)+"%]大于设定止损浮亏：[-"+nf.format(mon.getMaxLossRate() * 100)+"%]，建议卖出!";
				this.suggestionService.create(mon.getUserId(), invest_id, strategy_id, batchNum + 1, invest.getName(), remarks, String.valueOf(detail.getDetailId()), 
						TransactionType.SELL.value, inv.getAmount(), inv.getQuantity(), 
						new Date(), DateUtils.add(new Date(), Field.DATE, 5), null, "system", new Date());
			}
		} else if(mon.getMaxProfitRate() != null && mon.getMaxLossRate() != null) {	//回撤
			var summaryMax = this.summaryService.findByInvestIdForMaxProfit(invest_id);
			var summaryCur = this.summaryService.findByInvestId(invest_id, DateUtils.formatDate(DateUtils.add(new Date(), Field.DATE, -10), "yyyy-MM-dd")).getRows().get(0);
			var actualRate = (invest.getCost() - invest.getAmount()) / invest.getCost();
			var maxLossRate = (summaryMax.getProfit() - summaryCur.getProfit()) / summaryMax.getProfit();
			var maxProfitRate = summaryMax.getCost() > 0? summaryMax.getProfit() / summaryMax.getCost() : 1;
			if(maxProfitRate > mon.getMaxProfitRate() && maxLossRate > mon.getMaxLossRate()) {
				var inv = new Invest(invest.getAmount() * mon.getCommissionRate(), invest.getPrice(), 100.0);
				var detail = this.detailService.create(mon.getUserId(), mon.getTypeId(), invest_id, strategy_id, 
						TransactionType.SELL.value(), inv.getAmount(), inv.getQuantity(), invest.getPrice(), new Date());
				var remarks = "["+mon.getName()+"]" + "回撤监控，当前浮盈/浮亏["+nf.format(actualRate * 100)+"%]，最大浮盈：["+nf.format(maxProfitRate * 100)+"%]大于设定浮盈：["+nf.format(mon.getMaxProfitRate() * 100)+"%]，"
						+ "回撤浮盈：[-"+nf.format(maxLossRate * 100)+"%]大于设定回撤率：[-"+nf.format(mon.getMaxLossRate() * 100)+"%]，建议卖出!";
				this.suggestionService.create(mon.getUserId(), invest_id, strategy_id, batchNum + 1, invest.getName(), remarks, String.valueOf(detail.getDetailId()), 
						TransactionType.SELL.value, inv.getAmount(), inv.getQuantity(), 
						new Date(), DateUtils.add(new Date(), Field.DATE, 5), null, "system", new Date());
			}
		}
		return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
	}
	
	@Override
	public ActionResult flushDetails(Long strategy_id) {
		return null;
	}

}